package gui;

import java.awt.Font;
import java.awt.GridBagConstraints;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.JTextField;

import calculate.BinaryOperator;
import calculate.UnaryOperator;

/**
 * This class implements the template of the calculator. When you extend this
 * class you need to override all the methods below: 1.getKeyboard
 * 2.getBiOperator 3.getUnOperator 4.getFunctional 5.getKeys 6.Parser. And call
 * the construct method of this class to initialize the basic configurations.
 * When you finish all the steps above, you need to design the GUI in the
 * construct method of the subclass.
 * 
 * @author Jacky Xu
 *
 */
public abstract class Calculator {
	// The input buffer caching the input digit.
	protected String buffer = "";
	// The previous operator
	protected String pre_opt = "";

	// This part is the basic GUI. In a calculator, the panel is divided into three
	// parts: input line, output line and keyboard
	protected JTextField line1; // input line
	protected JTextField line2; // output line
	protected JPanel keyboard; // keyboard

	// This part defines the operator and corresponding action
	protected final HashMap<String, BinaryOperator> binaryoperators;
	protected final HashMap<String, UnaryOperator> unaryoperators;
	protected final HashMap<String, String> unaryoutput = new HashMap<String, String>();

	// This part defines the keys in the panel of the calculator.
	protected final String[] keys; // all the keys of the panel
	protected final List<String> functional; // the function keys, eg: back,AC etc
	protected final List<String> numbers = new ArrayList<String>(
			Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")); // all the numbers in the panel

	// The font of the input, output and keyboard. ( set 'Song style' as default
	protected Font font = new Font("宋体", Font.BOLD, 30);

	/**
	 * In the constructor, we will initialize the basic parts of the calculator.
	 * 
	 */
	public Calculator() {
		this.binaryoperators = this.getBiOperator();
		this.unaryoperators = this.getUnOperator();
		this.functional = this.getFunctional();
		this.keys = this.getKeys();

		this.line1 = getText("", this.font, false);
		this.line2 = getText("0", this.font, false);
		this.keyboard = getKeyboard(this.font);

	}

	/**
	 * This method will return a text field according to the input parameters
	 * 
	 * @param init:     the sequence first shown on the text field
	 * @param f:        the font
	 * @param editable: if the text field is editable
	 * @return an instance of the text field
	 */
	protected JTextField getText(String init, Font f, boolean editable) {
		JTextField temp = new JTextField(init);

		temp.setFont(f);
		temp.setEditable(editable);
		temp.setHorizontalAlignment(JTextField.RIGHT);

		return temp;
	}

	/**
	 * This method set the constraints
	 * 
	 * @param constrains
	 * @param gridx
	 * @param gridy
	 * @param gridwidth
	 * @param gridheight
	 * @param weightx
	 * @param weighty
	 */
	protected void setConstrain(GridBagConstraints constraints, int gridx, int gridy, int gridwidth, int gridheight,
			double weightx, double weighty) {
		constraints.gridx = gridx;
		constraints.gridy = gridy;
		constraints.gridwidth = gridwidth;
		constraints.gridheight = gridheight;
		constraints.weightx = weightx;
		constraints.weighty = weighty;
	}

	/**
	 * This method will return the keyboard panel according to the member variable
	 * keys
	 * 
	 * @param f: font the of keyboard
	 * @return the keyboard as the form of JPanel
	 */
	abstract protected JPanel getKeyboard(Font f);

	/**
	 * This method is to define all the binary operators
	 * 
	 * The operators are defined in package calculate, you should use the name of
	 * the operator as the key and the instance of the operator as the value and put
	 * the into a map.
	 * 
	 * @return a map as <name, instance of the operator>
	 */
	abstract protected HashMap<String, BinaryOperator> getBiOperator();

	/**
	 * This method is to define all the unary operators
	 * 
	 * The operators are defined in package calculate, you should use the name of
	 * the operator as the key and the instance of the operator as the value and put
	 * the into a map.
	 * 
	 * @return a map as <name, instance of the operator>
	 */
	abstract protected HashMap<String, UnaryOperator> getUnOperator();

	/**
	 * This method is to define all the functional keys of the calculator
	 * 
	 * The functional keys are the keys which are not related to the mathematical
	 * calculation process but related to the input or the output process. Such as
	 * the AC key, and the Back key
	 * 
	 * @return a list of the functional keys
	 */
	abstract protected List<String> getFunctional();

	/**
	 * This method defines all the keys in the calculator
	 * 
	 * Pay attention that the element in the array should be organized in order from
	 * the left-top to the right-down in the keyboard
	 * 
	 * @return an array which contains all the keys in order
	 */
	abstract protected String[] getKeys();

	/**
	 * This method defines the action when a button is pressed
	 * 
	 * @param command: the name of the button
	 */
	abstract protected void Parser(String command);
}
